/*	Copyright(C) 27wuyin 2019 */
//LGPL v2.1

#include<stdio.h>	//NULL......
#include<stdlib.h>
#include<string.h>	//strcpy
#include<time.h>	//time(),
#include"27log.h"

/*	some static(no link to any other file) var */
static logType_TS globalLog;
static char logType_Str[][64]={"LogType:memory"
							 ,"LogType:IO"
							 ,"LogType:Internet"
							 ,"LogType:dymic link library"
							 ,"LogType:data base library"
							 ,"LogType:calc"
							 ,"LogType:other/unknow"
							 };
static char _why[2048],_info[2048];
#define LOG_CANCHE 1
#define LOG_FREE   0

/*
	functionName:tsSetLogFile
	input:
		 log: a pointer to a log struct
		 name:the name of new log file
	output:none
	brief:Use this function to set the output log file name
*/
void tsSetLogFile(logType_TS *log,char *name)
{
	if(!log)
	{
		log=&globalLog;
	}
	strcpy(log->logFileName,name);

	return;
}

/*
	functionName:tsNewLogCtrl
	brief:Use this function to get a pointer to a new log struct
	in:none
	out:0:error(Error Code is in globalLog)
		other:a pointer to a new log struct
*/
logType_TS *tsNewLogCtrl(void)
{
	/*	alloction memory for the struct*/
	logType_TS *logPointer=malloc(sizeof(logType_TS));
	if(!logPointer)
	{
		tsThrowLog(_tsGlobalLog_Using,_MEMORY_LOG_,"Memory"
				  "alloction error","Library 27log,this "
				  "program try to get a new log struct."
				  "But memory alloction error.",TSFUNCTIONADDR_CAST(tsNewLogCtrl));
		tsAutoPrintLog(_tsGlobalLog_Using);
		return NULL;
	}

/*	Set some settings	*/
	logPointer->why=malloc(sizeof(char)*4096);
	logPointer->info=malloc(sizeof(char)*4096);
	logPointer->usingThis=LOG_FREE;
	strcpy(logPointer->logFileName,"log.log");

/*	Check memory */
	tsCheckPoint(_tsGlobalLog_Using,logPointer->why);
	tsAutoPrintLog(_tsGlobalLog_Using);
	tsCheckPoint(_tsGlobalLog_Using,logPointer->info);
	tsAutoPrintLog(_tsGlobalLog_Using);

	return logPointer;
}


/*	functionName:tsThrowLog
	brief:Use this function to make a log
	in:
		log:a point to the log struct(if it is NULL,
	this function will use the global log struct)
		why;why does this program make this log
		info:the info of the log
		functionAddr:an integer number of the fuction of
		making this log(it's an integer number,
		not a point!Use TSFUNCTIONADDR_CAST to have a cast)
*/

void tsThrowLog(logType_TS *log,logType_TYPE_TSLOG_ type
				,char *why,char *info
				,void *functionAddr)
{
	if(!log)			//using the global log struct
	{
		log=&globalLog;
		log->why=_why;
		log->info=_info;
		strcpy(log->logFileName,"log.log");
	}

	//copy the info of the log
	strcpy(log->why,why);
	strcpy(log->info,info);

	//which function makes this log
	log->whereFunction=functionAddr;

	//get the time and save it to the struct
	log->logTime=time(&(log->logTime_S));

	//save the log type
	log->type=type;

	//using flag
	log->usingThis=LOG_CANCHE;

	return;
}

/*
	functionName:printLog_Stream
	input:a point to the struct of log
	output:none
	brief:Use this function to print the log to
	the stream in a format way.
*/
static void printLog_Stream(logType_TS *log,FILE *stream)
{
	/*	Print the info of log */
	fprintf(stream,"LogTime:%lld(ms),",log->logTime);
	fputs(ctime(&(log->logTime_S)),stream);
	fputs("Type:",stream);
	fputs(logType_Str[log->type],stream);
	fputs("\nWhy:",stream);
	fputs(log->why,stream);

	fputs("\nFunctionAddress:",stream);
	fprintf(stream,"0x%p\n",log->whereFunction);
	fputs("Info:",stream);
	fputs(log->info,stream);
	fputc('\n',stream);

	return;
}

/*
	functionName:tsAutoPrintLog
	input&output:none
	brief:
		print the log to a file or stderr
*/
void tsAutoPrintLog(logType_TS *log)
{
	if(!log)
	{
		log=&globalLog;
	}

	if(log->usingThis==LOG_FREE)
	{
		return;
	}

	/*	check and open the log file */
	FILE *logFile=fopen(log->logFileName,"a");
	if(!logFile)
	{
		logFile=fopen(log->logFileName,"w");
		if(!logFile)
		{
			logFile=stderr;
			fputs("\nDo not worry about it."
				  "The program cannot open the log file."
				  "So this log print to stderr.\n",stderr);
			printLog_Stream(log,stderr);

			/*	Clean the using flag */
			log->usingThis=LOG_FREE;
			return;
		}
	}

	printLog_Stream(log,logFile);
	printLog_Stream(log,stderr);

	/*	Clean the using flag */
	log->usingThis=LOG_FREE;

	fclose(logFile);

	return;
}
/*
	functionName:tsPrintLogStdErr
	input:
		log:a pointer to the struct of log.
	output:none
	brief:Use this function to print log
	to the stderr.
*/
void tsPrintLogStdErr(logType_TS *log)
{
	if(!log)
	{
		log=&globalLog;
	}
	printLog_Stream(log,stderr);

	return;
}


/*
	functionName:tsGetLogInfo
	output:none
	brief:Use this function to get the info this log.
*/
void tsGetLogInfo(logType_TS *log,logType_TYPE_TSLOG_ *logType
				 ,char *why,char *info,void **function
				 ,long long int *logTime)
{
	/*	If log's value is NULL,use global log */
	if(!log)
	{
		log=&globalLog;
	}

	/*	write the data user need */
	if(logType)
	{
		*logType=log->type;
	}
	if(why)
	{
		strcpy(why,log->why);
	}
	if(info)
	{
		strcpy(info,log->info);
	}
	if(function)
	{
		*function=log->whereFunction;
	}
	if(logTime)
	{
		*logTime=log->logTime;
	}

	return;
}

/*	functionName:tsFreeLogPtr
	input:
		  log:a pointer to a log struct
	output:none
	brief:Use this function to free a log struct
*/
void tsFreeLogPtr(logType_TS *log)
{
	free(log->info);
	free(log->why);
	free(log);

	return;
}

int tsIsLogThrew(logType_TS *log)
{
	if(!log)
	{
		log=&globalLog;
	}

	return log->usingThis==LOG_FREE?0:1;
}

